home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / HPACK78S.ZIP / gui.c < prev    next >
C/C++ Source or Header  |  1992-12-03  |  24KB  |  881 lines

  1. /****************************************************************************
  2. *                                                                            *
  3. *                            HPACK Multi-System Archiver                        *
  4. *                            ===========================                        *
  5. *                                                                            *
  6. *                                HPACK GUI Interface                            *
  7. *                              GUI.C  Updated 06/05/92                        *
  8. *                                                                            *
  9. * This program is protected by copyright and as such any use or copying of    *
  10. *  this code for your own purposes directly or indirectly is highly uncool    *
  11. *                      and if you do so there will be....trubble.                *
  12. *                 And remember: We know where your kids go to school.            *
  13. *                                                                            *
  14. *            Copyright 1992  Peter C.Gutmann.  All rights reserved            *
  15. *                                                                            *
  16. ****************************************************************************/
  17.  
  18. /* "Another Brick Through the Window, Part 2"
  19.  
  20.     {\lead}
  21.     We don't need no pull-down-menus
  22.     We don't need no rescaled fonts
  23.     No dark icons in the corner
  24.     Hackers, leave those Macs alone.
  25.     Hey! Hackers! Leave them Macs alone!
  26.     All in all its just another WIMP up for sale
  27.     All in all you're just another WIMP up for the sale.
  28.  
  29.     {\kids}
  30.     We don't need no fancy windows
  31.     We don't need no title bars
  32.     No MultiFinder in the startup
  33.     Hackers leave them Macs alone
  34.     Hey! Hackers! Leave them Macs alone!
  35.     All in all its just another WIMP up for sale
  36.     All in all you're just another WIMP up for the sale.
  37.  
  38.     {\guitar}
  39.  
  40.     "Another Brick Through the Window, Part 3"
  41.  
  42.     I don't need no mice around me
  43.     And I don't need no fonts to calm me.
  44.     I have seen the writing on the wall.
  45.     Don't think I need any WIMP at all.
  46.     No! Don't think I need any WIMP at all.
  47.     No! Don't think I'll need any WIMP at all.
  48.     All in all it was all just bricks through the window.
  49.     All in all you were all just bricks through the window.
  50.  
  51.         - Nathan Torkington */
  52.  
  53. #include <ctype.h>
  54. #include <string.h>
  55. #include <time.h>
  56. #ifdef __MAC__
  57.   #include "defs.h"
  58.   #include "arcdir.h"
  59.   #include "choice.h"
  60.   #include "error.h"
  61.   #include "filesys.h"
  62.   #include "flags.h"
  63.   #include "frontend.h"
  64.   #include "hpacklib.h"
  65.   #include "system.h"
  66.   #include "wildcard.h"
  67.   #include "crypt.h"
  68.   #include "fastio.h"
  69. #else
  70.   #include "defs.h"
  71.   #include "arcdir.h"
  72.   #include "choice.h"
  73.   #include "error.h"
  74.   #include "filesys.h"
  75.   #include "flags.h"
  76.   #include "frontend.h"
  77.   #include "hpacklib.h"
  78.   #include "system.h"
  79.   #include "wildcard.h"
  80.   #include "crypt/crypt.h"
  81.   #include "io/fastio.h"
  82. #endif /* __MAC__ */
  83.  
  84. /* Prototypes for functions in SCRIPT.C */
  85.  
  86. void addFilespec( char *fileSpec );
  87. void freeFilespecs( void );
  88. void processListFile( const char *listFileName );
  89.  
  90. /* Prototypes for compression functions */
  91.  
  92. void initPack( const BOOLEAN initAllBuffers );
  93. void endPack( void );
  94.  
  95. /* The following are defined in ARCHIVE.C */
  96.  
  97. extern BOOLEAN overWriteEntry;    /* Whether we overwrite the existing file
  98.                                    entry or add a new one in addFileHeader()
  99.                                    - used by FRESHEN, REPLACE, UPDATE */
  100.  
  101. /* The default match string, which matches all files.  This must be done
  102.    as a static array since if given as a constant string some compilers
  103.    may put it into the code segment which may cause problems when FILESYS.C
  104.    attempts any conversion on it */
  105.  
  106. char WILD_MATCH_ALL[] = "*";
  107.  
  108. /* The following is normally declared in VIEWFILE.C, but is used somewhat
  109.    differently by the GUI code */
  110.  
  111. int dateFormat = 0;
  112.  
  113. #ifdef __MSDOS__
  114. /* The drive the archive is on (needed for multidisk archives) */
  115.  
  116. extern BYTE archiveDrive;
  117. #endif /* __MSDOS__ */
  118.  
  119. /****************************************************************************
  120. *                                                                            *
  121. *                            Serious Error Handler                            *
  122. *                                                                            *
  123. ****************************************************************************/
  124.  
  125. #if 0
  126.  
  127. void error( ERROR_INFO *errInfo, ... )
  128.     {
  129.     /*
  130.     ** Handle any error recovery as in error.c (in the Mac's case, just
  131.     ** crashing would probably be appropriate).  Depending on the severity
  132.     ** you would exit back to the OS or just complain via an idiot box.
  133.     */
  134.     }
  135. #endif /* 0 */
  136.  
  137. /****************************************************************************
  138. *                                                                            *
  139. *                                Idiot Boxes                                    *
  140. *                                                                            *
  141. ****************************************************************************/
  142.  
  143. void alert( ALERT_TYPE alertType, const char *message )
  144.     {
  145.     puts( "vvvvvvvv" );
  146.     switch( alertType )
  147.         {
  148.         case ALERT_FILE_IN_ARCH:
  149.             printf( "File %s already in archive - skipping\n", message );
  150.             break;
  151.  
  152.         case ALERT_TRUNCATED_PADDING:
  153.             printf( "Truncated %s bytes of EOF padding\n", message );
  154.             break;
  155.  
  156.         case ALERT_DATA_ENCRYPTED:
  157.             printf( "Data is encrypted - skipping %s\n", message );
  158.             break;
  159.  
  160.         case ALERT_DATA_CORRUPTED:
  161.             puts( "Warning: Data probably corrupted" );
  162.             break;
  163.  
  164.         case ALERT_PATH_TOO_LONG:
  165.             printf( "Path %s... too long\n", message );
  166.             break;
  167.  
  168.         case ALERT_CANNOT_PROCESS_ENCR_INFO:
  169.             printf( "Cannot process encryption information - skipping %s\n", message );
  170.             break;
  171.  
  172.         case ALERT_CANNOT_OPEN_DATAFILE:
  173.             printf( "Cannot open data file %s - skipping\n", message );
  174.             break;
  175.  
  176.         case ALERT_CANNOT_OPEN_SCRIPTFILE:
  177.             printf( "Cannot open script file %s\n", message );
  178.             break;
  179.  
  180.         case ALERT_UNKNOWN_ARCH_METHOD:
  181.             printf( "Unknown archiving method - skipping %s\n", message );
  182.             break;
  183.  
  184.         case ALERT_NO_OVERWRITE_EXISTING:
  185.             printf( "Won't overwrite existing file %s\n", message );
  186.             break;
  187.  
  188.         case ALERT_FILE_IS_DEVICEDRVR:
  189.             puts( "File corresponds to device driver" );
  190.             break;
  191.  
  192.         case ALERT_ARCHIVE_UPTODATE:
  193.             puts( "Archive is uptodate" );
  194.             break;
  195.  
  196.         case ALERT_ERROR_DURING_ERROR_REC:
  197.             puts( "Communist plot detected.  Archiver suiciding" );
  198.             break;
  199.  
  200.         case ALERT_UNKNOWN_SCRIPT_COMMAND:
  201.             printf( "Unknown script command %s\n", message );
  202.             break;
  203.  
  204.         case ALERT_PATH_TOO_LONG_LINE:
  205.             printf( "Path too long in line %s\n", message );
  206.             break;
  207.  
  208.         case ALERT_BAD_CHAR_IN_FILENAME_LINE:
  209.             printf( "Bad char in filename on line %s\n", message );
  210.             break;
  211.  
  212.         case ALERT_CANT_FIND_PUBLIC_KEY:
  213.             puts( "Cannot find public key" );
  214.             break;
  215.  
  216.         case ALERT_SEC_INFO_CORRUPTED:
  217.             puts( "Security information corrupted" );
  218.             break;
  219.  
  220.         case ALERT_SECRET_KEY_COMPROMISED:
  221.             printf( "Secret key compromised for userID %s\n", message );
  222.             puts( "This public key cannot be used" );
  223.             break;
  224.  
  225.         case ALERT_ARCH_SECTION_TOO_SHORT:
  226.             puts( "Archive section too short, skipping..." );
  227.             break;
  228.  
  229.         case ALERT_DIRNAME_CONFLICTS_WITH_FILE:
  230.             printf( "Directory name %s conflicts with existing file - skipping\n", message );
  231.             break;
  232.  
  233.         case ALERT_PASSWORD_INCORRECT:
  234.             puts( "Password is incorrect" );
  235.             break;
  236.  
  237.         case ALERT_FILES_CORRUPTED:
  238.             if( !strcmp( message, "1" ) )
  239.                 puts( "One file was corrupted" );
  240.             else
  241.                 printf( "%d files were corrupted\n", message );
  242.             break;
  243.         }
  244.  
  245.     puts( "^^^^^^^^" );
  246.     }
  247.  
  248. /****************************************************************************
  249. *                                                                            *
  250. *                                Various Dialog Boxes                        *
  251. *                                                                            *
  252. ****************************************************************************/
  253.  
  254. /* Conio function prototypes */
  255.  
  256. int getch( void );        /* getchar() is brain-damaged */
  257. void clrscr( void );    /* Clear screen */
  258.  
  259. /* Handle an idiot box - either return TRUE or FALSE to the caller, or
  260.    exit if response is FALSE */
  261.  
  262. BOOLEAN confirmAction( const char *message )
  263.     {
  264.     /*
  265.     ** Return TRUE or FALSE depending on whether the luser selects TRUE or
  266.     ** FALSE based on the message prompt.  See CLI equivalent in ARCHIVE.C
  267.     */
  268.     char ch;
  269.  
  270.     puts( "vvvvvvvv" );
  271.     printf( "%s: y/n\n", message );
  272.     ch = getch();
  273.     puts( "^^^^^^^^" );
  274.     return( ( ch == 'y' ) ? TRUE : FALSE );
  275.     }
  276.  
  277. void idiotBox( const char *message )
  278.     {
  279.     /*
  280.     ** Either return or exit depending on whether the luser selects TRUE or
  281.     ** FALSE based on the message prompt.  See CLI equivalent in ARCHIVE.C
  282.     */
  283.     char ch;
  284.  
  285.     puts( "vvvvvvvv" );
  286.     printf( "Warning: %s. Continue y/n\n", message );
  287.     ch = getch();
  288.     puts( "^^^^^^^^" );
  289.  
  290.     if( ch == 'n' )
  291.         {
  292.         errorFD = IO_ERROR;    /* Make sure we don't delete archive by mistake */
  293.         error( STOPPED_AT_USER_REQUEST );
  294.         }
  295.     }
  296.  
  297. /* Confirm skipping a file */
  298.  
  299. BOOLEAN confirmSkip( const char *str1, const char *str2, const BOOLEAN str1Filename )
  300.     {
  301.     /*
  302.     ** Confirm skipping/overwriting an existing file.  See CLI equivalent
  303.     ** in ARCHIVE.C
  304.     */
  305.     char ch;
  306.  
  307.     if( str1Filename );        /* Get rid of warning */
  308.     puts( "vvvvvvvv" );
  309.     printf( "%s %s, y/n/a\n", str1, str2 );
  310.     ch = getch();
  311.     puts( "^^^^^^^^" );
  312.  
  313.     if( ch == 'n' )
  314.         return( TRUE );
  315.  
  316.     if( ch == 'a' )
  317.         {
  318.         /* Do an automatic "Confirm" for all following files */
  319.         if( flags & INTERACTIVE )
  320.             flags &= ~INTERACTIVE;
  321.         else
  322.             overwriteFlags |= OVERWRITE_ALL;
  323.         }
  324.  
  325.     return( FALSE );
  326.     }
  327.  
  328. /* Get a filename from the luser */
  329.  
  330. void getFileName( char *fileName )
  331.     {
  332.     printf( "%s already exists:  Enter new filename\n", fileName );
  333.     gets( fileName );
  334.     }
  335.  
  336. /* Highly complex dialog box used to handle multidisk archive I/O */
  337.  
  338. void multipartWait( const WORD promptType, const int partNo )
  339.     {
  340.     /*
  341.     ** See the equivalent function in FASTIO.C for more details
  342.     */
  343.  
  344.     puts( "vvvvvvvv" );
  345.     if( promptType & WAIT_PARTNO )
  346.         printf( "This is part %d of a multipart archive\n", partNo + 1 );
  347.     else
  348.         putchar( '\n' );
  349.     printf( "Please insert the " );
  350.     if( promptType & WAIT_NEXTDISK )
  351.         printf( "next disk" );
  352.     else
  353.         if( promptType & WAIT_PREVDISK )
  354.             printf( "previous disk" );
  355.         else
  356.             {
  357.             printf( "disk containing " );
  358.             if( promptType & WAIT_LASTPART )
  359.                 printf( "the last part" );
  360.             else
  361.                 printf( "part %d", partNo + 1 );
  362.             printf( " of this archive" );
  363.             }
  364.     printf( " and press a key\n" );
  365.     getch();
  366.     puts( "^^^^^^^^" );
  367.     }
  368.  
  369. /* Get a password from the luser, no echo */
  370.  
  371. char *getPassword( char *passWord, const char *prompt )
  372.     {
  373.     puts( "vvvvvvvv" );
  374.     printf( prompt );
  375.     gets( passWord );
  376.     putchar( '\n' );
  377.     puts( "^^^^^^^^" );
  378.     return( passWord );
  379.     }
  380.  
  381. /* Display the status of a signature on a piece of data.  The good signature
  382.    message is an information-type message, the bad signature message is an
  383.    alert()-type message */
  384.  
  385. void showSig( const BOOLEAN isGoodSig, const char *userID, const LONG timeStamp )
  386.     {
  387.     puts( "vvvvvvvv" );
  388.     if( isGoodSig )
  389.         printf( "Good signature from %s\n", userID );
  390.     else
  391.         printf( "Bad signature from %s\n", userID );
  392.     printf( "Signature made on %s\n", ctime( ( time_t * ) &timeStamp ) );
  393.     puts( "^^^^^^^^" );
  394.     }
  395.  
  396. /****************************************************************************
  397. *                                                                            *
  398. *                            Compression Progress Report                        *
  399. *                                                                            *
  400. ****************************************************************************/
  401.  
  402. static long totalLength, currentLength;
  403.  
  404. /* Display the initial compression progress bar */
  405.  
  406. void initProgressReport( const BOOLEAN fileNameTruncated, const char *oldFileName, \
  407.                          const char *fileName, const LONG fileLen )
  408.     {
  409.     /*
  410.     ** Initialise a progress indicator for the file fileName of length
  411.     ** fileSize.  If fileNameTrunc is TRUE then the filename has been
  412.     ** truncated from oldFileName
  413.     */
  414.  
  415.     /* Save total data length */
  416.     totalLength = fileLen;
  417.     currentLength = 0L;
  418.  
  419.     puts( "vvvvvvvv" );
  420.     printf( "Initing progress bar: %ld bytes\n", fileLen );
  421.     printf( "Processing " );
  422.     if( fileNameTruncated )
  423.         printf( "%s as ", oldFileName );
  424.     printf( "%s\n", fileName );
  425.     puts( "^^^^^^^^" );
  426.     }
  427.  
  428. /* Update progress bar */
  429.  
  430. void updateProgressReport( void )
  431.     {
  432.     /*
  433.     ** Update progress bar.  Called for every 2K between 0...totalLength
  434.     */
  435.  
  436.     /* Update data length */
  437.     currentLength += 2048;
  438.  
  439.     puts( "vvvvvvvv" );
  440.     printf( "Updating progress bar: %d%% complete\n", \
  441.              ( currentLength * 100 ) / totalLength );
  442.     puts( "^^^^^^^^" );
  443.     }
  444.  
  445. /* Erase progress bar */
  446.  
  447. void endProgressReport( void )
  448.     {
  449.     /*
  450.     ** Erase progress bar
  451.     */
  452.  
  453.     puts( "vvvvvvvv" );
  454.     puts( "Ending progress bar" );
  455.     puts( "^^^^^^^^" );
  456.     }
  457.  
  458. /****************************************************************************
  459. *                                                                            *
  460. *                                Main Program Code                            *
  461. *                                                                            *
  462. ****************************************************************************/
  463.  
  464. #ifdef __MSDOS__
  465.  
  466. /* Kludge code for DOS */
  467.  
  468. void *hmalloc( const unsigned long noBytes )
  469.     {
  470.     return( malloc( ( size_t ) noBytes ) );
  471.     }
  472.  
  473. void *hmallocSeg( const WORD noBytes )
  474.     {
  475.     /* TurboC uses 8 bytes for the MCB, so we just add 1 to the segment
  476.        to get a paragraph-aligned address */
  477.     return( MK_FP( FP_SEG( malloc( noBytes + 8 ) ) + 1, 0 ) );
  478.     }
  479.  
  480. void hfree( void *pointer )
  481.     {
  482.     /* Check if it's a normalised pointer */
  483.     if( !( FP_OFF( pointer ) & 0x000F ) )
  484.         /* Yes, call free with true address of MCB */
  485.         free( MK_FP( FP_SEG( pointer ) - 1, 8 ) );
  486.     else
  487.         /* No, just free it */
  488.         free( pointer );
  489.     }
  490. #endif /* __MSDOS__ */
  491.  
  492. /* Global vars used by the frontend code */
  493.  
  494. BOOLEAN hasFileSpec = FALSE, createNew = FALSE;
  495. char archivePath[ MAX_PATH ], fileCode[ MATCH_DEST_LEN ];
  496.  
  497. /* Get and validate the archive name */
  498.  
  499. void getArchiveName( void )
  500.     {
  501.     char archiveName[ 80 ];
  502.     int lastSlash, i;
  503.  
  504.     puts( "vvvvvvvv" );
  505.     printf( "Enter archive name " );
  506.     gets( archiveName );
  507.     putchar( '\n' );
  508.     puts( "^^^^^^^^" );
  509.  
  510.     /* Check archive name */
  511.     if( strlen( archiveName ) > MAX_PATH - 5 )
  512.         error( PATH_s_TOO_LONG, archiveName );
  513.     else
  514.         strcpy( archiveFileName, archiveName );
  515.  
  516.     /* Extract the pathname and convert it into an OS-compatible format */
  517.     lastSlash = extractPath( archiveFileName, archivePath );
  518. #ifdef __MSDOS__
  519.     archiveDrive = ( archiveFileName[ 1 ] == ':' ) ? toupper( *archiveFileName ) - 'A' + 1 : 0;
  520. #endif /* __MSDOS__ */
  521.  
  522.     /* Either append archive file suffix to the filespec or force the
  523.        existing suffix to HPAK_EXT */
  524.     for( i = lastSlash; archiveFileName[ i ] && archiveFileName[ i ] != '.'; i++ );
  525.     if( archiveFileName[ i ] != '.' )
  526.         strcat( archiveFileName, HPAK_EXT );
  527.     else
  528.         strcpy( archiveFileName + i, HPAK_EXT );
  529.     }
  530.  
  531. /* Get and process the filename */
  532.  
  533. void getArchFilename( void )
  534.     {
  535.     char fileName[ 80 ];
  536.     FILEINFO fileInfo;
  537.     int lastSlash;
  538.     char ch;
  539.     BOOLEAN fileHasWildcards;
  540.  
  541.     puts( "vvvvvvvv" );
  542.     printf( "Enter file name " );
  543.     gets( fileName );
  544.     putchar( '\n' );
  545.     puts( "^^^^^^^^" );
  546.  
  547.     /* Add the name of the file to be processed to the fileName list */
  548.     if( *fileName == '@' )
  549.         {
  550.         /* Assemble the listFile name + path into the dirBuffer (we can't
  551.            use the mrglBuffer since processListFile() ovrewrites it) */
  552.         strcpy( ( char * ) dirBuffer, fileName + 1 );
  553.         lastSlash = extractPath( ( char * ) dirBuffer, ( char * ) dirBuffer + 80 );
  554.         compileString( ( char * ) dirBuffer + lastSlash, ( char * ) dirBuffer + 80 );
  555.         fileHasWildcards = strHasWildcards( ( char * ) dirBuffer + 80 );
  556. #if defined( __MSDOS__ ) || defined( __OS2__ )
  557.         if( lastSlash && ( ch = dirBuffer[ lastSlash - 1 ] ) != SLASH && \
  558.                                                           ch != ':' )
  559. #else
  560.         if( lastSlash && ( ch = dirBuffer[ lastSlash - 1 ] ) != SLASH )
  561. #endif /* __MSDOS__ || __OS2__ */
  562.             /* Add a slash if necessary */
  563.             dirBuffer[ lastSlash - 1 ] = SLASH;
  564.         strcpy( ( char * ) dirBuffer + lastSlash, MATCH_ALL );
  565.  
  566.         /* Check each file in the directory, passing those that match to
  567.            processListFile() */
  568.         if( findFirst( ( char * ) dirBuffer, FILES, &fileInfo ) )
  569.             {
  570.             do
  571.                 {
  572.                 strcpy( ( char * ) dirBuffer + lastSlash, fileInfo.fName );
  573.                 if( matchString( ( char * ) dirBuffer + 80, fileInfo.fName, fileHasWildcards ) )
  574.                     processListFile( ( char * ) dirBuffer );
  575.                 }
  576.             while( findNext( &fileInfo ) );
  577.             findEnd( &fileInfo );
  578.             }
  579.         }
  580.     else
  581.         addFilespec( fileName );
  582.     hasFileSpec = TRUE;
  583.     }
  584.  
  585. /* Display the archive directory, allowing manipulation of the files/dirs */
  586.  
  587. typedef enum { SEL_PARENTDIR, SEL_SUBDIR, SEL_FILE } SELECTTYPE;
  588.  
  589. void showArchiveDirectory( void )
  590.     {
  591.     FILEHDRLIST *fileInfoPtr, *currFileInfoPtr;
  592.     DIRHDRLIST *dirInfoPtr, *currDirInfoPtr;
  593.     WORD count, nextDir;
  594.     int line, currLine = 0, ch;
  595.     SELECTTYPE currentSelection;
  596.     LONG theTime;
  597.  
  598.     /* First, sort the files in each directory */
  599.     for( count = getFirstDir(); count != END_MARKER; count = getNextDir() )
  600.         sortFiles( count );
  601.  
  602.     count = getFirstDir();
  603.     while( TRUE )
  604.         {
  605.         currentSelection = 0;
  606.         clrscr();
  607.         line = 0;
  608.  
  609.         /* Display a parent direcotory if we're not on the root dir */
  610.         if( count != ROOT_DIR )
  611.             {
  612.             if( line == currLine )
  613.                 currentSelection = SEL_PARENTDIR;
  614.  
  615.             printf( ( line == currLine ) ? "-> " : "   " );
  616.             printf( ( getDirTaggedStatus( count ) ) ? "#" : " " );
  617.             printf( " %15s ", ".." );
  618.             theTime = getDirTime( count );
  619.             printf( ctime( ( time_t * ) &theTime ) );
  620.             line++;
  621.             }
  622.  
  623.         /* First display directories */
  624.         if( ( dirInfoPtr = getFirstDirEntry( count ) ) != NULL )
  625.             do
  626.                 {
  627.                 if( line == currLine )
  628.                     {
  629.                     currentSelection = SEL_SUBDIR;
  630.                     currDirInfoPtr = dirInfoPtr;
  631.                     }
  632.  
  633.                 printf( ( line == currLine ) ? "-> " : "   " );
  634.                 printf( ( dirInfoPtr->tagged ) ? "#" : " " );
  635.                 printf( " %15s ", dirInfoPtr->dirName );
  636.                 printf( ctime( ( time_t * ) &dirInfoPtr->data.dirTime ) );
  637.                 line++;
  638.                 }
  639.             while( ( dirInfoPtr = getNextDirEntry() ) != NULL );
  640.  
  641.         /* Then display files */
  642.         if( ( fileInfoPtr = getFirstFileEntry( count ) ) != NULL )
  643.             do
  644.                 /* Exclude special-format files */
  645.                 if( !( fileInfoPtr->data.archiveInfo & ARCH_SPECIAL ) )
  646.                     {
  647.                     if( line == currLine )
  648.                         {
  649.                         currentSelection = SEL_FILE;
  650.                         currFileInfoPtr = fileInfoPtr;
  651.                         }
  652.  
  653.                     printf( ( line == currLine ) ? "-> " : "   " );
  654.                     printf( ( fileInfoPtr->tagged ) ? "#" : " " );
  655.                     printf( " %15s ", fileInfoPtr->fileName );
  656.                     printf( ctime( ( time_t * ) &fileInfoPtr->data.fileTime ) );
  657.                     line++;
  658.                     }
  659.             while( ( fileInfoPtr = getNextFileEntry() ) != NULL );
  660.  
  661.         ch = toupper( getch() );
  662.         if( !ch )
  663.             ch = getch();
  664.         if( ch == 0x48 && currLine )
  665.             currLine--;
  666.         else
  667.             if( ch == 0x50 && currLine < line - 1 )
  668.                 currLine++;
  669.             else
  670.                 if( ch == '\r' )
  671.                     {
  672.                     /* Go up/down a directory level */
  673.                     if( currentSelection == SEL_PARENTDIR )
  674.                         {
  675.                         count = getParent( count );
  676.                         currLine = 0;
  677.                         }
  678.                     if( currentSelection == SEL_SUBDIR && \
  679.                         ( nextDir = getNextDir() ) != END_MARKER )
  680.                         {
  681.                         count = nextDir;
  682.                         currLine = 0;
  683.                         }
  684.                     setArchiveCwd( count );
  685.                     }
  686.                 else
  687.                     if( ch == ' ' )
  688.                         {
  689.                         if( currentSelection == SEL_SUBDIR )
  690.                             selectDir( currDirInfoPtr, !currDirInfoPtr->tagged );
  691.                         if( currentSelection == SEL_FILE )
  692.                             selectFile( currFileInfoPtr, !currFileInfoPtr->tagged );
  693.                         }
  694.                     else
  695.                         if( ch == 'Q' )
  696.                             break;
  697.         }
  698.     }
  699.  
  700. /* The main program */
  701.  
  702. void main( void )
  703.     {
  704.     BOOLEAN doProcess = FALSE;
  705.     char ch;
  706.  
  707.     /* Perform general initialization */
  708.     initFastIO();
  709. #ifndef __OS2__
  710.     initExtraInfo();
  711. #endif /* __OS2__ */
  712.     getScreenSize();
  713.     initPack( TRUE );
  714.     initArcDir();
  715.  
  716.     /* Reset general status vars */
  717.     hasFileSpec = FALSE;
  718.  
  719.     /* Process commands */
  720.     while( TRUE )
  721.         {
  722.         clrscr();
  723.         puts( "Main menu: Choose [C]ommand, [O]ption, [A]rchive name, [F]ile name, [G]o" );
  724.         ch = getch();
  725.         switch( toupper( ch ) )
  726.             {
  727.             case 'C':
  728.                 puts( "Enter command [A]dd, [T]est, [N]ew" );
  729.                 choice = toupper( getch() );
  730.                 if( choice == 'N' )
  731.                     {
  732.                     choice = 'A';
  733.                     createNew = TRUE;
  734.                     }
  735.                 break;
  736.  
  737.             case 'O':
  738.                 puts( "Setting option to OVERWRITE_SRC" );
  739.                 flags |= OVERWRITE_SRC;
  740.                 break;
  741.  
  742.             case 'A':
  743.                 getArchiveName();
  744.                 break;
  745.  
  746.             case 'F':
  747.                 getArchFilename();
  748.                 break;
  749.  
  750.             case 'G':
  751.                 doProcess = TRUE;
  752.             }
  753.  
  754.         /* Check if user has selected 'Go' */
  755.         if( doProcess )
  756.             {
  757.             doProcess = FALSE;
  758.  
  759.             /* Default is all files if no name is given */
  760.             if( !hasFileSpec )
  761.                 addFilespec( WILD_MATCH_ALL );
  762.  
  763.             /* Set up the encryption system */
  764.             initCrypt();
  765.  
  766.             /* Reset error stats and arcdir system */
  767.             archiveFD = errorFD = dirFileFD = secFileFD = IO_ERROR;
  768.             oldArcEnd = 0L;
  769.             resetArcDir();
  770.             overWriteEntry = FALSE;
  771.             if( flags & BLOCK_MODE )
  772.                 firstFile = TRUE;
  773.  
  774.             /* Open archive file with various types of mungeing depending on the
  775.                command type */
  776.             if( choice == ADD || choice == UPDATE )
  777.                 {
  778.                 /* Create a new archive if none exists or overwrite an existing
  779.                    one if asked for */
  780.                 if( createNew )
  781.                     {
  782.                     errorFD = archiveFD = hcreat( archiveFileName, CREAT_ATTR );
  783.                     strcpy( errorFileName, archiveFileName );
  784.                     setOutputFD( archiveFD );
  785.                     }
  786.                 else
  787.                     if( ( archiveFD = hopen( archiveFileName, O_RDWR | S_DENYRDWR | A_SEQ ) ) != IO_ERROR )
  788.                         {
  789.                         setInputFD( archiveFD );
  790.                         setOutputFD( archiveFD );
  791.                         readArcDir( SAVE_DIR_DATA );
  792.  
  793.                         /* If we are adding to an existing archive and run out
  794.                            of disk space, we must cut back to the size of the
  795.                            old archive.  We do this by remembering the old state
  796.                            of the archive and restoring it to this state if
  797.                            necessary */
  798.                         if( fileHdrStartPtr != NULL )
  799.                             /* Only set oldArcEnd if there are files in the archive */
  800.                             oldArcEnd = fileHdrCurrPtr->offset + fileHdrCurrPtr->data.dataLen + \
  801.                                         fileHdrCurrPtr->data.auxDataLen;
  802.                         getArcdirState( ( FILEHDRLIST ** ) &oldHdrlistEnd, &oldDirEnd );
  803.  
  804.                         /* Move past existing data */
  805.                         hlseek( archiveFD, oldArcEnd + HPACK_ID_SIZE, SEEK_SET );
  806.                         }
  807.                 }
  808.             else
  809.                 {
  810.                 /* Try to open the archive for read/write (in case we need to
  811.                    truncate X/Ymodem padding bytes.  If that fails, open it
  812.                    for read-only */
  813.                 if( ( archiveFD = hopen( archiveFileName, O_RDWR | S_DENYWR | A_RANDSEQ ) ) == IO_ERROR )
  814.                     archiveFD = hopen( archiveFileName, O_RDONLY | S_DENYWR | A_RANDSEQ );
  815.                 setInputFD( archiveFD );
  816.                 if( archiveFD != IO_ERROR )
  817.                     readArcDir( choice != VIEW && choice != EXTRACT && \
  818.                                 choice != TEST && choice != DISPLAY  );
  819.                 }
  820.  
  821.             /* Make sure the open was successful */
  822.             if( archiveFD == IO_ERROR )
  823.                 error( CANNOT_OPEN_ARCHFILE, archiveFileName );
  824.  
  825.             /* Show the directory and allow file tagging if there is an
  826.                archive directory */
  827.             if( !createNew )
  828.                 showArchiveDirectory();
  829.  
  830.             /* Now munge files to/from archive */
  831.             handleArchive();
  832.  
  833.             /* The UPDATE command is just a combination of the ADD and FRESHEN
  834.                commands.  We handle it by first adding new files in one pass
  835.                (but not complaining when we strike duplicates like ADD does),
  836.                and then using the freshen command to update duplicates in a
  837.                second pass.  This leads to a slight problem in that when files
  838.                are added they will then subsequently match on the freshen pass;
  839.                hopefully the extra overhead from checking whether they should
  840.                be freshened will not be too great */
  841.             if( choice == UPDATE )
  842.                 {
  843.                 choice = FRESHEN;
  844.                 oldArcEnd = 0L;    /* Make sure we don't try any ADD-style recovery
  845.                                    since errorFD is now the temp file FD */
  846.                 vlseek( 0L, SEEK_SET );    /* Go back to start of archive */
  847.                 handleArchive();
  848.                 }
  849.  
  850.             /* Perform any updating of extra file information on the archive
  851.                unless it's a multipart archive, which leads to all sorts of
  852.                complications */
  853.             if( archiveChanged && !( flags & MULTIPART_ARCH ) )
  854.                 setExtraInfo( archiveFD );
  855.  
  856.             /* Perform cleanup functions for this file */
  857.             if( choice != DELETE && choice != FRESHEN && choice != REPLACE )
  858.                 /* If DELETE, FRESHEN, or REPLACE, archive file is already closed */
  859.                 {
  860.                 hclose( archiveFD );
  861.                 archiveFD = IO_ERROR;    /* Mark it as invalid */
  862.                 }
  863.  
  864.             /* Only go through the loop once if we are either creating a new
  865.                archive or processing a multipart archive */
  866.             if( createNew || flags & MULTIPART_ARCH )
  867.                 break;
  868.             }
  869.         }
  870.  
  871.     /* Clean up before exiting */
  872.     if( choice != DELETE )
  873.         endPack();
  874.     endCrypt();
  875.     endArcDir();
  876.     endExtraInfo();
  877.     endFastIO();
  878.     freeFilespecs();
  879.     freeArchiveNames();
  880.     }
  881.